﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Model;
using Model.Admin;
using Model.RepositoryInterfaces;
using NHibernate;
using Type = System.Type;

namespace DAL.Repositories
{
    public class ApartmentRepository:IApartmentRepository
    {
        private string _stringQuery = "from Apartment";
        private bool _optionalParam = false;
        private bool _needsAnd = false;

        public void Create(Apartment entity)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.Save(entity);
                        transaction.Commit();
                    }
                    catch (ADOException exception)
                    {
                        throw new Exception(exception.Message);
                    }
                }
            }
        }

        public ApartmentDetailsViewModel Read(int id)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var apartment = session.Get<Apartment>(id);

                if(apartment != null)
                {
                    var roomsViewModel = new List<RoomViewModel>();
                    foreach (var room in apartment.Rooms)
                    {
                        var roomViewModel = new RoomViewModel()
                        {
                            Id = room.Id,
                            RoomGroup = room.RoomGroup,
                            Mark = room.Mark,
                            Type = room.Type.Name,
                            Name = room.Name,
                            Area = room.Area
                        };
                        roomsViewModel.Add(roomViewModel);
                    }

                    var detailsViewModel = new ApartmentDetailsViewModel()
                    {
                        Id = apartment.Id,
                        Code = apartment.Code,
                        Building = apartment.Building,
                        State = apartment.State.Name,
                        Floor = apartment.Floor.Name,
                        RoomsNumber = apartment.RoomsNumber.Name,
                        Facade = apartment.Facade.Name,
                        Price = Math.Round(apartment.Price, 2),
                        TotalPrice = Math.Round(apartment.TotalPrice1, 2),
                        Area = apartment.Area,
                        Rooms = roomsViewModel
                    };

                    return detailsViewModel;
                }
                else
                {
                    return new ApartmentDetailsViewModel();
                }
            }
        }

        public void Update(Apartment apartment)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.Update(apartment);

                        var updatedApartment = session.Get<Apartment>(apartment.Id);
                        updatedApartment.IndexTable.Code = apartment.Code;
                        updatedApartment.IndexTable.Floor = apartment.Floor.Name;
                        updatedApartment.IndexTable.RoomsNumber = apartment.RoomsNumber.Name;
                        updatedApartment.IndexTable.Facade = apartment.Facade.Name;

                        session.Update(updatedApartment);

                        transaction.Commit();
                    }
                    catch (ADOException exception)
                    {
                        throw new Exception(exception.Message);
                    }
                }
            }
        }

        public Apartment ReadAdmin(int id)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var apartment = session.Get<Apartment>(id);
                return apartment;
            }
        }

        public IList<ApartmentViewModel> Search(int floorId, int roomsNumberId, int areaId, int priceId)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var floor = session.Get<Floor>(floorId);
                var roomsNumber = session.Get<RoomsNumber>(roomsNumberId);
                var areaInterval = session.Get<AreaInterval>(areaId);
                var priceInterval = session.Get<PriceInterval>(priceId);

                if(floor.Name != "sve")
                {
                    CheckWhere();
                    CheckAnd();
                    _stringQuery += "Floor_id = :floorId";
                }
                if(roomsNumber.Name != "sve")
                {
                    CheckWhere();
                    CheckAnd();
                    _stringQuery += "RoomsNumber_id = :roomsNumberId";
                }
                if(areaInterval.Name != "sve")
                {
                    CheckWhere();
                    CheckAnd();
                    _stringQuery += "Area between :lowerLimit and :upperLimit";
                }
                if (priceInterval.Name != "sve")
                {
                    CheckWhere();
                    CheckAnd();
                    _stringQuery += "Price between :lowerPrice and :upperPrice";
                }

                //dohvati id status prodani stan u šifrarniku
                var specialQueryString = "from CodeList where ClassType = 'State' and Name = 'prodano'";
                var specialQuery = session.CreateQuery(specialQueryString);
                var prodanState = specialQuery.UniqueResult<State>();
                var prodanId = prodanState.Id; 

                //dohvati samo neprodane stanove 
                CheckWhere();
                CheckAnd();
                _stringQuery += "State_id <> :stateId";

                var query = session.CreateQuery(_stringQuery);
                //query.SetMaxResults(15);

                if(floor.Name != "sve")
                {
                    query.SetParameter("floorId", floorId);
                }
                if(roomsNumber.Name != "sve")
                {
                    query.SetParameter("roomsNumberId", roomsNumberId);
                }
                if(areaInterval.Name != "sve")
                {
                    query.SetParameter("lowerLimit", areaInterval.LowerLimit).SetParameter("upperLimit", areaInterval.UpperLimit);
                }
                if (priceInterval.Name != "sve")
                {
                    query.SetParameter("lowerPrice", priceInterval.LowerLimit).SetParameter("upperPrice", priceInterval.UpperLimit);
                }
                query.SetParameter("stateId", prodanId);
                
                var apartments = query.List<Apartment>();

                var viewModels = new List<ApartmentViewModel>();
                foreach (var apartment in apartments)
                {
                    var statePrice = CheckState(apartment);
                    var viewModel = new ApartmentViewModel()
                    {
                        Id = apartment.Id,
                        Code = apartment.Code,
                        Floor = apartment.Floor.Name,
                        RoomsNumber = apartment.RoomsNumber.Name,
                        Area = Math.Round(apartment.Area, 2),
                        StatePrice = statePrice,
                        TotalPrice = Math.Round(apartment.TotalPrice1, 2)
                    };
                    viewModels.Add(viewModel);
                }
                
                return viewModels;
            }
        }

        

        public IList<ApartmentViewModel> SearchByKeyword(string keyword)
        {
            var viewModels = new List<ApartmentViewModel>();

            using (var session = NHibernateHelper.OpenSession())
            {
                var sql = String.Format("SELECT Apartment_id FROM {0} WHERE DIFFERENCE(Code, {1}) = 4 OR DIFFERENCE(Floor, {1}) >= 3 OR DIFFERENCE(RoomsNumber, {1}) >= 3 OR DIFFERENCE(Facade, {1}) >= 3", "ApartmentSearch", "'" + keyword + "'");
                var query = session.CreateSQLQuery(sql);
                //query.SetMaxResults(15);
                var identificators = query.List<int>();

                foreach (var identificator in identificators)
                {
                    var apartment = session.Get<Apartment>(identificator);
                    if(apartment.State.Name != "prodano")
                    {
                        var statePrice = CheckState(apartment);
                        var viewModel = new ApartmentViewModel()
                        {
                            Id = apartment.Id,
                            Code = apartment.Code,
                            Floor = apartment.Floor.Name,
                            RoomsNumber = apartment.RoomsNumber.Name,
                            Area = Math.Round(apartment.Area, 2),
                            StatePrice = statePrice,
                            TotalPrice = Math.Round(apartment.TotalPrice1, 2)
                        };
                        viewModels.Add(viewModel);
                    }
                }
            }
            return viewModels;
        }

        public IList<ApartmentByFloorViewModel> GetBasicInfoByFloor(int floorId)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var floor = session.Get<Floor>(floorId);
                
                if (floor.Name != "sve")
                {
                    _stringQuery += " where Floor_id = :floorId";
                }
                
                var query = session.CreateQuery(_stringQuery);
                
                if (floor.Name != "sve")
                {
                    query.SetParameter("floorId", floorId);
                }

                var apartments = query.List<Apartment>();

                var viewModels = new List<ApartmentByFloorViewModel>();
                foreach (var apartment in apartments)
                {
                    var viewModel = new ApartmentByFloorViewModel()
                    {
                        Id = apartment.Id,
                        Code = apartment.Code,
                        Model = apartment.Model,
                        State = apartment.State.Name,
                        Price = Math.Round(apartment.Price, 2)
                    };
                    viewModels.Add(viewModel);
                }

                return viewModels;
            }
        }

        private string CheckState(Apartment apartment)
        {
            if (apartment.State.Name.Equals("prodano"))
            {
                return apartment.State.Name;
            }
            else
            {
                var price = Math.Round(apartment.Price, 2);
                return price.ToString();
            }
        }

        public IList<ApartmentListViewModel> ReadAll()
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var query = session.CreateQuery("from Apartment");
                var list = query.List<Apartment>();

                //IList<Apartment> orderedApartments = list.OrderBy(a => a.Code).ToList();

                //var orderedApartments = list.Reverse();

                var apartmentsViewModel = new List<ApartmentListViewModel>();
                foreach (var apartment in list)
                {
                    var apartmentViewModel = new ApartmentListViewModel()
                    {
                        Id = apartment.Id,
                        Code = apartment.Code,
                        Building = apartment.Building,
                        State = apartment.State.Name,
                        Floor = apartment.Floor.Name,
                        RoomsNumber = apartment.RoomsNumber.Name,
                        Facade = apartment.Facade.Name,
                        Model = apartment.Model,
                        Price = apartment.Price,
                        Area = apartment.Area
                    };
                    apartmentsViewModel.Add(apartmentViewModel);
                }
                
                return apartmentsViewModel;
            }
        }

        public IList<ApartmentListViewModel> ReadByCode(string code)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var query = session.CreateQuery("from Apartment where Code = :code").SetParameter("code", code);
                var list = query.List<Apartment>();
                var apartmentsViewModel = new List<ApartmentListViewModel>();
                foreach (var apartment in list)
                {
                    var apartmentViewModel = new ApartmentListViewModel()
                    {
                        Id = apartment.Id,
                        Code = apartment.Code,
                        Building = apartment.Building,
                        State = apartment.State.Name,
                        Floor = apartment.Floor.Name,
                        RoomsNumber = apartment.RoomsNumber.Name,
                        Facade = apartment.Facade.Name,
                        Model = apartment.Model,
                        Price = apartment.Price,
                        Area = apartment.Area
                    };
                    apartmentsViewModel.Add(apartmentViewModel);
                }

                return apartmentsViewModel;
            }
        }

        public IList<ApartmentListViewModel> ReadByModel(string model)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var query = session.CreateQuery("from Apartment where Model = :model").SetParameter("model", model);
                var list = query.List<Apartment>();
                var apartmentsViewModel = new List<ApartmentListViewModel>();
                foreach (var apartment in list)
                {
                    var apartmentViewModel = new ApartmentListViewModel()
                    {
                        Id = apartment.Id,
                        Code = apartment.Code,
                        Building = apartment.Building,
                        State = apartment.State.Name,
                        Floor = apartment.Floor.Name,
                        RoomsNumber = apartment.RoomsNumber.Name,
                        Facade = apartment.Facade.Name,
                        Model = apartment.Model,
                        Price = apartment.Price,
                        Area = apartment.Area
                    };
                    apartmentsViewModel.Add(apartmentViewModel);
                }

                return apartmentsViewModel;
            }
        }

        public IList<RoomViewModel> ReadRooms(int id)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var apartment = session.Get<Apartment>(id);

                var roomsViewModel = new List<RoomViewModel>();
                foreach (var room in apartment.Rooms)
                {
                    var roomViewModel = new RoomViewModel()
                    {
                        Id = room.Id,
                        RoomGroup = room.RoomGroup,
                        Mark = room.Mark,
                        Type = room.Type.Name,
                        Name = room.Name,
                        Area = room.Area
                    };
                    roomsViewModel.Add(roomViewModel);
                }

                return roomsViewModel;
            }
        }

        public void Delete(int id)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        var apartment = session.Get<Apartment>(id);
                        session.Delete(apartment);
                        transaction.Commit();
                    }
                    catch (ADOException exception)
                    {
                        throw new Exception(exception.Message);
                    }
                }
            }
        }

        public void AddRoom(int id, Room room)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        var apartment = session.Get<Apartment>(id);
                        apartment.Rooms.Add(room);
                        session.Update(apartment);
                        transaction.Commit();
                    }
                    catch (ADOException exception)
                    {
                        throw new Exception(exception.Message);
                    }
                }
            }
        }

        public void UpdateRoom(int apartmentId, int roomId, RoomCrudViewModel viewModel)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        var apartment = session.Get<Apartment>(apartmentId);

                        for (int i = 0; i < apartment.Rooms.Count; i++)
                        {
                            if(apartment.Rooms[i].Id == roomId)
                            {
                                var codeListRep = new CodeListRepository();
                                var selectedType = (Model.Type)codeListRep.Read(viewModel.SelectedType);

                                apartment.Rooms[i].RoomGroup = (int)viewModel.RoomGroup;
                                apartment.Rooms[i].Mark = viewModel.Mark;
                                apartment.Rooms[i].Type = selectedType;
                                apartment.Rooms[i].Name = viewModel.Name;
                                apartment.Rooms[i].Area = (double)viewModel.Area;

                                break;
                            }
                        }
                        
                        session.Update(apartment);
                        transaction.Commit();
                    }
                    catch (ADOException exception)
                    {
                        throw new Exception(exception.Message);
                    }
                }
            }
        }

        public void DeleteRoom(int apartmentId, int roomId)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        var apartment = session.Get<Apartment>(apartmentId);

                        for (int i = 0; i < apartment.Rooms.Count; i++)
                        {
                            if(apartment.Rooms[i].Id == roomId)
                            {
                                apartment.Rooms.RemoveAt(i);
                                break;
                            }
                        }

                        session.Update(apartment);
                        transaction.Commit();
                    }
                    catch (ADOException exception)
                    {
                        throw new Exception(exception.Message);
                    }
                }
            }
        }

        public Room ReadRoom(int id)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                var room = session.Get<Room>(id);
                return room;
            }
        }

        public void GenerateRoomsByModel(int apartmentId)
        {
            using (var session = NHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        var mainApartment = session.Get<Apartment>(apartmentId);

                        var queryApartments = session.CreateQuery("from Apartment where Model = :model and Id <> :apartmentId")
                            .SetParameter("model", mainApartment.Model)
                            .SetParameter("apartmentId", mainApartment.Id); ;
                        var apartments = queryApartments.List<Apartment>();

                        var queryRooms = session.CreateQuery("from Room where Apartment_id = :apartmentId")
                            .SetParameter("apartmentId", mainApartment.Id);
                        var rooms = queryRooms.List<Room>();

                        Room newRoom;
                        foreach (var apartment in apartments)
                        {
                            foreach (var existingRoom in rooms)
                            {
                                newRoom = new Room()
                                {
                                    RoomGroup = existingRoom.RoomGroup,
                                    Mark = existingRoom.Mark,
                                    Type = existingRoom.Type,
                                    Name = existingRoom.Name,
                                    Area = existingRoom.Area
                                };
                                apartment.Rooms.Add(newRoom);
                            }
                            
                            session.Update(apartment);
                        }

                        transaction.Commit();
                    }
                    catch (ADOException exception)
                    {
                        throw new Exception(exception.Message);
                    }
                }
            }
        }

        public void CheckWhere()
        {
            if (_optionalParam == false)
            {
                _optionalParam = true;
                _stringQuery += " where ";
            }
        }

        public void CheckAnd()
        {
            if(_needsAnd == false)
            {
                _needsAnd = true;
            }
            else
            {
                _stringQuery += " and ";
            }
        }


        
    }
}
